<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			svg {
				border: 1px solid #CCCCCC;
			}
			foreignObject { overflow: visible; }

			.model {
				/* font-size: 12px;
				transform: scale(0.63,0.63) ;
				*font-size: 10px; */
				/* background-color: #0000FF; */
				border: 2px solid #323232;
				border-radius: 4px;
				/* position: absolute; */
				background-color: #fff;
				-webkit-user-select: none;
				-moz-user-select: none;
				-ms-user-select: none;
				user-select: none;

			}

			.tablename:hover {
				/* background-color: #F0F8FF; */
				cursor: move;
			}

			.model .tablename {
				font-size: 10px;
				font-weight: bold;
				border-bottom: 2px solid #323232;
				padding: 2px 3px 3px 2px;
				text-align: center;
			}

			.model .columns {
				padding: 5px;
			}

			.model .columns ul {
				list-style-type: none;
				padding: 0px;
				margin: 0px;
				font-size: 10px;
				/* font-weight: 500; */
			}

			.model .columns ul li {
				text-indent: 2px;
				overflow: hidden;
				/* min-height: 18px; */
			}
		</style>
	</head>
	<body>
		<svg id="s" version="1.1" baseProfile="full" width="100%" height="1000" xmlns="http://www.w3.org/2000/svg">
			<!-- <g id="g1" transform="translate(100,100)">
				<foreignObject class="node" x="0" y="0" width="200px" height="100%">
					<div id="box" class="model">
						<div class="tablename">
							子公司月度销售金额（不含税）<br />
							DM_SALES_MONTH_AMOUNT
						</div>
						<div class="columns">
							<ul>
								<li>🔑 COMPANY_CODE  -  公司代码</li>
								<li>🔑 COMPANY_NAME  -  公司名称</li>
								<li>COMOPANY_SHORTNAME  -  公司简称</li>
								<li>ACCOUNT_TITLE_CATEGORY  -  科目分类 </li>
								<li>YEAR_MONTH  -  年月</li>
								<li>AMOUNT - 金额</li>
								<li>DAY  -  过账日期</li>
								<li>FISAL_YEAR  -  会计年度</li>
							</ul>
						</div>
					</div>
				</foreignObject>
			</g> -->
			<!-- <circle cx="300" cy="220" r="5" stroke="black" stroke-width="1" fill="#fff" /> -->

			<!-- <g id="g2" transform="translate(400,100)">
				<foreignObject class="node" x="0" y="0" width="200px" height="300px">
					<div id="box" class="model">
						<div class="tablename">
							子公司月度销售金额（不含税）<br />
							DM_SALES_MONTH_AMOUNT
						</div>
						<div class="columns">
							<ul>
								<li>🔑 COMPANY_CODE  -  公司代码</li>
								<li>🔑 COMPANY_NAME  -  公司名称</li>
								<li>COMOPANY_SHORTNAME  -  公司简称</li>
								<li>ACCOUNT_TITLE_CATEGORY  -  科目分类 </li>
								<li>YEAR_MONTH  -  年月</li>
								<li>AMOUNT - 金额</li>
								<li>DAY  -  过账日期</li>
								<li>FISAL_YEAR  -  会计年度</li>
							</ul>
						</div>
					</div>
				</foreignObject>
			</g> -->
			<!-- <circle cx="400" cy="220" r="5" stroke="black" stroke-width="1" fill="fff" /> -->
			<defs>
				<marker id='markerArrow' markerWidth='13' markerHeight='13' refx='2' refy='6' orient='auto'>
					<path d='M2,2 L2,11 L10,6 L2,2' style='fill:#000' />
				</marker>
			</defs>


			<!-- <line x1='100' y1='50' x2='200' y2='50' stroke='#000' stroke-width='1' marker-end='url(#markerArrow)'>
			</line> -->

			<!-- <line x1='100' y1='50' x2='300' y2='150' stroke='#000' stroke-width='1' marker-end='url(#markerArrow)'>
			</line> -->

		</svg>
	</body>
</html>

<script type="text/javascript">
	var SVG_NS = "http://www.w3.org/2000/svg"
	var svg = document.getElementById("s");
	/**
	 * 表模型头部
	 */
	class TabelModelHeader {
		constructor(tableName, comment) {
			this.element = document.createElement("div")
			this.element.setAttribute("class", "tablename")
			this.element.innerHTML = tableName + "<br/>" + comment;
		}
		toElement() {
			return this.element;
		}
	}

	/**
	 * 表模型字段主体
	 */
	class TableModelColumns {
		constructor(columns) {
			if (!columns) throw "TableModelColumns.constructor：无效的参数"
			this.element = document.createElement("div")
			this.element.setAttribute("class", "columns")
			let columnDoc = this.makeList(columns)
			this.element.appendChild(columnDoc)
		}
		makeList(columns) {
			let ul = document.createElement("ul")
			for (var i = 0; i < columns.length; i++) {
				let column = columns[i]
				let li = document.createElement("li")
				li.innerHTML = (column.primaryKey ? "🔑" : "") + " " + column.columnName + "  -  " + column
					.columnComment;
				ul.appendChild(li);
			}
			return ul;
		}
		toElement() {
			return this.element;
		}
	}
	class TableModel {
		constructor(tableStruct) {
			this.model = document.createElement("div")
			this.model.setAttribute("class", "model")
			let h = new TabelModelHeader(tableStruct.tableName, tableStruct.tableComment);
			let cs = new TableModelColumns(tableStruct.columns);
			this.appendChild(h)
			this.appendChild(cs)
		}
		appendChild(object) {
			this.model.appendChild(object.toElement())
		}
		
		clientHeight(){
			return this.model.clientHeight;
		}

		toElement() {
			return this.model;
		}
	}

	/**
	 * HTML容器，在svg中嵌入HTML时使用
	 */
	class ForeignObject {
		constructor(arg) {
			this.foreignObject = document.createElementNS(SVG_NS, "foreignObject")
			this.foreignObject.setAttribute("class", "node")
			this.foreignObject.setAttribute("x", 0)
			this.foreignObject.setAttribute("y", 0)
			this.foreignObject.setAttribute("width", "200px")
			// this.foreignObject.setAttribute("height", "300px")
		}

		toElement() {
			return this.foreignObject;
		}
		
		getTableModel(){
			return this.tableModel;
		}
		
		setHeight(height){
			this.foreignObject.getAttribute("height",height+"px");
		}

		appendChild(object) {
			if (typeof(object) != "object") throw "子元素类型不正确"
			this.tableModel = object;
			this.foreignObject.appendChild(this.tableModel.toElement())
		}
	}

	var template = {
		x: 0,
		y: 0,
		meta: {
			tableName: "input_wms_entry",
			tableComment: "WMS入库",
			columns: [{
					"columnName": "ID",
					"primaryKey": true,
					"columnComment": "ID"
				},
				{
					"columnName": "Latest_storage_documents",
					"primaryKey": false,
					"columnComment": "最新入库单据"
				},
				{
					"columnName": "Document_number",
					"primaryKey": false,
					"columnComment": "单据编号"
				},
				{
					"columnName": "document_type",
					"primaryKey": false,
					"columnComment": "单据类型"
				}
			]
		}
	}

	/**
	 * 连接点
	 */
	class ConnectPoint {
		constructor(cx, cy) {
			//<circle cx="400" cy="220" r="5" stroke="black" stroke-width="1" fill="fff" />
			this.el = document.createElementNS(SVG_NS, "circle")
			this.el.setAttribute("cx", cx)
			this.el.setAttribute("cy", cy)
			this.el.setAttribute("stroke", "black")
			this.el.setAttribute("stroke-width", "1")
			this.el.setAttribute("fill", "#fff")
			this.el.setAttribute("r", "4")
		}

		hide() {
			this.el.setAttribute("r","0")
		}
		show() {
			this.el.setAttribute("r","4")
		}

		move(x, y) {
			this.el.setAttribute("cx", x)
			this.el.setAttribute("cy", y)
		}

		toElement() {
			return this.el;
		}
	}

	/**
	 * 连接点组
	 */
	class ConnectPointGroup {

		/**
		 * 连接点父节点的坐标，这里是指表模型的坐标
		 * @param {Object} x
		 * @param {Object} y
		 */
		constructor(x, y,h) {
			this.h = h;
			this.make(x, y,h)
		}

		hide() {
			this.top.hide();
			this.bottom.hide();
			this.left.hide();
			this.right.hide();
		}
		show() {
			this.top.show();
			this.bottom.show();
			this.left.show();
			this.right.show();
		}

		make(x, y,h) {
			let cpl = this.connectPointLocation(x, y,h)
			this.top = new ConnectPoint(cpl.top.x, cpl.top.y)
			this.bottom = new ConnectPoint(cpl.bottom.x, cpl.bottom.y)
			this.right = new ConnectPoint(cpl.right.x, cpl.right.y)
			this.left = new ConnectPoint(cpl.left.x, cpl.left.y)
		}

		install() {
			svg.appendChild(this.top.toElement())
			svg.appendChild(this.bottom.toElement())
			svg.appendChild(this.right.toElement())
			svg.appendChild(this.left.toElement())
		}

		move(x, y) {
			let cpl = this.connectPointLocation(x, y)
			this.top.move(cpl.top.x, cpl.top.y)
			this.bottom.move(cpl.bottom.x, cpl.bottom.y)
			this.right.move(cpl.right.x, cpl.right.y)
			this.left.move(cpl.left.x, cpl.left.y)
		}

		connectPointLocation(x, y) {
			let top = {
				x: x + 100,
				y: y
			};
			let bottom = {
				x: x + 100,
				y: y + (this.h)
			};
			let right = {
				x: x,
				y: y + (this.h/2)
			};
			let left = {
				x: x + 200,
				y: y + (this.h/2)
			};
			return {
				top: top,
				bottom: bottom,
				left: left,
				right: right
			};
		}
	}

	class Table {
		constructor(data) {
			svg = document.getElementById("s");
			this.svg = svg;
			//安装主体
			let el = this.makeTable(data.meta,data.x,data.y);
			this.el = el;
			let self = this;
			//设置连接点
			let position = this.getPosition()
			this.connectPointGroup = new ConnectPointGroup(position.x, position.y,this.height);
			//安装连接点
			this.connectPointGroup.install();
			 //默认隐藏连接点
			this.connectPointGroup.hide()
			

			//监听事件
			var pt = svg.createSVGPoint(),doc = svg.ownerDocument;
			var root = doc.rootElement || doc.body || svg;
			var xlate, txStartX, txStartY, mouseStart;
			var xforms = el.transform.baseVal;

			//鼠标划过事件
			el.onmouseover=function(evt) {
				//显示连接点
				self.connectPointGroup.show()
			}
			//鼠标移开事件
			el.onmouseout=function(evt) {
				self.connectPointGroup.hide()
			}

			//鼠标按下事件
			el.addEventListener('mousedown', startMove, false);

			function startMove(evt) {
				//显示连接点
				self.connectPointGroup.show()
				root.addEventListener('mousemove', handleMove, false);
				root.addEventListener('mouseup', finishMove, false);

				xlate = xforms.numberOfItems > 0 && xforms.getItem(0);
				if (!xlate || xlate.type != SVGTransform.SVG_TRANSFORM_TRANSLATE) {
					xlate = xforms.createSVGTransformFromMatrix(svg.createSVGMatrix());
					xforms.insertItemBefore(xlate, 0);
				}

				txStartX = xlate.matrix.e;
				txStartY = xlate.matrix.f;
				mouseStart = inElementSpace(evt);
				fireEvent('dragstart');

				//W3C阻止冒泡方法 
				evt = evt || window.event;
				if (evt.stopPropagation) {
					evt.stopPropagation();
				} else {
					evt.cancelBubble = true; //IE阻止冒泡方法  
				}
			}

			function handleMove(evt) {
				var point = inElementSpace(evt);
				let x = txStartX + point.x - mouseStart.x;
				let y = txStartY + point.y - mouseStart.y;
				xlate.setTranslate(x, y);
				//移动连接点
				self.dragCallback(x, y)
				fireEvent('drag');
			}

			function finishMove(evt) {
				root.removeEventListener('mousemove', handleMove, false);
				root.removeEventListener('mouseup', finishMove, false);
				fireEvent('dragend');
			}

			function fireEvent(eventName) {
				var event = new Event(eventName);
				event.detail = {
					x: xlate.matrix.e,
					y: xlate.matrix.f
				};
				return el.dispatchEvent(event);
			}

			// Convert mouse position from screen space to coordinates of el
			function inElementSpace(evt) {
				pt.x = evt.clientX;
				pt.y = evt.clientY;
				return pt.matrixTransform(el.parentNode.getScreenCTM().inverse());
			}
		}

		dragCallback(x, y) {
			console.log("实时位置：" + x + "," + y)
			this.connectPointGroup.move(x, y)
		}

		makeTable(meta,x,y) {
			this.g = document.createElementNS(SVG_NS, "g")
			this.g.setAttribute("transform", "translate("+x+","+y+")")
			//创建foreignobject
			let foreignObject = new ForeignObject();
			//创建model
			let tm = new TableModel(meta)
			foreignObject.appendChild(tm);
			this.g.appendChild(foreignObject.toElement());
			//安装
			this.svg.appendChild(this.g);
			console.log(tm.model.clientHeight)
			this.height = tm.model.clientHeight;
			//设置模型容器高度
			foreignObject.setHeight(tm.model.clientHeight)
			return this.g;
		}

		getPosition() {
			let transform = this.el.getAttribute("transform");
			let xy = transform.replace("translate(", "").replace(")", "").split(",");
			return {
				x: parseInt(xy[0]),
				y: parseInt(xy[1])
			}
		}
	}

	/**
	 * 元素拖拽注册
	 * @param {Object} el  要被拖动的元素对象
	 * @Depared
	 */
	// function makeDraggable(el,callback) {
	// 	if (!el) return console.error('makeDraggable() needs an element');
	// 	var svg = el;
	// 	while (svg && svg.tagName != 'svg') svg = svg.parentNode;
	// 	if (!svg) return console.error(el, 'must be inside an SVG wrapper');
	// 	var pt = svg.createSVGPoint(),
	// 		doc = svg.ownerDocument;

	// 	var root = doc.rootElement || doc.body || svg;
	// 	var xlate, txStartX, txStartY, mouseStart;
	// 	var xforms = el.transform.baseVal;

	// 	el.addEventListener('mousedown', startMove, false);

	// 	function startMove(evt) {
	// 		console.log(this)
	// 		// We listen for mousemove/up on the root-most
	// 		// element in case the mouse is not over el.
	// 		root.addEventListener('mousemove', handleMove, false);
	// 		root.addEventListener('mouseup', finishMove, false);

	// 		// Ensure that the first transform is a translate()
	// 		xlate = xforms.numberOfItems > 0 && xforms.getItem(0);
	// 		if (!xlate || xlate.type != SVGTransform.SVG_TRANSFORM_TRANSLATE) {
	// 			xlate = xforms.createSVGTransformFromMatrix(svg.createSVGMatrix());
	// 			xforms.insertItemBefore(xlate, 0);
	// 		}

	// 		txStartX = xlate.matrix.e;
	// 		txStartY = xlate.matrix.f;
	// 		mouseStart = inElementSpace(evt);
	// 		fireEvent('dragstart');

	// 		//W3C阻止冒泡方法 
	// 		evt = evt || window.event;
	// 		if (evt.stopPropagation) {
	// 			evt.stopPropagation();
	// 		} else {
	// 			evt.cancelBubble = true; //IE阻止冒泡方法  
	// 		}
	// 	}

	// 	function handleMove(evt) {
	// 		var point = inElementSpace(evt);
	// 		let x = txStartX + point.x - mouseStart.x;
	// 		let y = txStartY + point.y - mouseStart.y;
	// 		xlate.setTranslate(x,y);
	// 		//回调
	// 		if(typeof(callback)=='function'){
	// 			callback(x,y)
	// 		}
	// 		fireEvent('drag');
	// 	}

	// 	function finishMove(evt) {
	// 		root.removeEventListener('mousemove', handleMove, false);
	// 		root.removeEventListener('mouseup', finishMove, false);
	// 		fireEvent('dragend');
	// 	}

	// 	function fireEvent(eventName) {
	// 		var event = new Event(eventName);
	// 		event.detail = {
	// 			x: xlate.matrix.e,
	// 			y: xlate.matrix.f
	// 		};
	// 		return el.dispatchEvent(event);
	// 	}

	// 	// Convert mouse position from screen space to coordinates of el
	// 	function inElementSpace(evt) {
	// 		pt.x = evt.clientX;
	// 		pt.y = evt.clientY;
	// 		return pt.matrixTransform(el.parentNode.getScreenCTM().inverse());
	// 	}
	// }

	// makeDraggable(document.getElementById("g1"))
	// makeDraggable(document.getElementById("g2"))

	//画线	
	function makeLine(el) {
		if (!el) return console.error('makeDraggable() needs an element');
		var svg = el;
		while (svg && svg.tagName != 'svg') svg = svg.parentNode;
		if (!svg) return console.error(el, 'must be inside an SVG wrapper');
		var pt = svg.createSVGPoint(),
			doc = svg.ownerDocument;

		var root = doc.rootElement || doc.body || svg;
		var xlate, txStartX, txStartY, mouseStart;
		var xforms = el.transform.baseVal;

		el.addEventListener('mousedown', startMove, false);

		var poyline = null;

		function startMove(evt) {
			//线的起点
			poyline = document.createElementNS(SVG_NS, 'line');

			//poyline.setAttribute('id', "polyline-id")

			let startX = evt.clientX - 10;
			let startY = evt.clientY - 9;
			//搜索附近的连接点，并吸附
			let nebarConnect = getNearbyConnectPoint(evt.clientX, evt.clientY);
			if (nebarConnect) {
				startX = nebarConnect.x;
				startY = nebarConnect.y;
			}
			poyline.setAttribute('x1', startX)
			poyline.setAttribute('y1', startY)
			poyline.setAttribute('x2', startX)
			poyline.setAttribute('y2', startY)
			poyline.setAttribute("marker-end", "url(#markerArrow)")
			poyline.setAttribute("style", "fill:none;stroke:#000;stroke-width:2");
			svg.appendChild(poyline);
			root.addEventListener('mousemove', handleMove, false);
			root.addEventListener('mouseup', finishMove, false);

			function handleMove(evt) {
				let endX = evt.clientX - 10;
				let endY = evt.clientY - 10;
				//搜索附近的连接点，并吸附
				let nebarConnect = getNearbyConnectPoint(endX, endY);
				if (nebarConnect) {
					endX = nebarConnect.x;
					endY = nebarConnect.y;
				}
				poyline.setAttribute("x2", endX);
				poyline.setAttribute("y2", endY);
			}

			function finishMove(evt) {

				let endX = evt.clientX - 10;
				let endY = evt.clientY - 10;
				//搜索附近的连接点，并吸附
				let nebarConnect = getNearbyConnectPoint(endX, endY);
				if (nebarConnect) {
					//吸附
					endX = nebarConnect.x;
					endY = nebarConnect.y;
				}
				
				//将线转换为折线 todo
				
				poyline.setAttribute("x2", endX);
				poyline.setAttribute("y2", endY);

				root.removeEventListener('mousemove', handleMove, false);
				root.removeEventListener('mouseup', finishMove, false);
				//fireEvent('dragend');
			}

			function fireEvent(eventName) {
				var event = new Event(eventName);
				event.detail = {
					x: xlate.matrix.e,
					y: xlate.matrix.f
				};
				return el.dispatchEvent(event);
			}

			// Convert mouse position from screen space to coordinates of el
			function inElementSpace(evt) {
				// pt.x = evt.clientX;
				// pt.y = evt.clientY;
				// return pt.matrixTransform(el.parentNode.getScreenCTM().inverse());
			}
		}
	}

	/**
	 * 搜索最近的连接点
	 * @param {Object} x
	 * @param {Object} y
	 */
	function getNearbyConnectPoint(x, y) {
		let cps = document.getElementsByTagName("circle");
		for (let i = 0; i < cps.length; i++) {
			let cp = cps[i];
			let cpX = cp.getAttribute("cx");
			let cpY = cp.getAttribute("cy");
			//console.log("X:" + x + "附近目标：" + cpX)
			//console.log("Y:" + y + "附近目标：" + cpY)
			if (Math.abs(cpX - x) < 20 && Math.abs(cpY - y) < 20) {
				return {
					x: cpX,
					y: cpY
				};
			}
		}
		return null;
	}
	makeLine(document.getElementById("s"))
	let t = new Table(template);
	let t2 = new Table(template);
</script>
